<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title></title>
    <script src="https:d3js.org/d3.v3.min.js"></script>
  </head>
  <style>
    .forceLine {
      stroke: #444;
      stroke-width: 2;
    }

    .forceCircle {
      stroke: black;
      stroke-width: 2;
    }

    .forceText {
      fill: black;
      text-anchor: middle;
      font-size: 20;
      font-family: arial;
    }
  </style>

  <body>
    <script>
      var width = 600;
      var height = 600;
      var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);

      // 初始数据
      var nodes = [
        { name: "0" },
        { name: "1" },
        { name: "2" },
        { name: "3" },
        { name: "4" },
        { name: "5" },
        { name: "6" },
      ];
      var edges = [
        { source: 0, target: 1 },
        { source: 0, target: 2 },
        { source: 0, target: 3 },
        { source: 1, target: 4 },
        { source: 1, target: 5 },
        { source: 1, target: 6 },
      ];

      // 转换数据
      var force = d3.layout
        .force() // 创建一个力导向图布局
        .nodes(nodes) // 节点
        .links(edges) // 定义边
        .size([width, height]) // 定义作用范围
        .linkDistance(90) // 定义连线的距离
        .charge(-400); // 定义电荷数，相互排斥
      force.start(); // 开启布局计算

      //3.绘制
      var color = d3.scale.category20();

      //绘制连线
      var lines = svg
        .selectAll(".forceLine")
        .data(edges)
        .enter()
        .append("line")
        .attr("class", "forceLine");

      //绘制节点
      var circles = svg
        .selectAll(".forceCircle")
        .data(nodes)
        .enter()
        .append("circle")
        .attr("class", "forceCircle")
        .attr("r", 30)
        .style("fill", function (d, i) {
          return color(i);
        })
        .call(force.drag); // 力向导图的拖拽行为

      var drag = force.drag()   // 定义拖拽行为，可以不定义
                    .on("dragstart", function (d) {
                    //拖拽开始后设定被拖拽对象为固定
                    d.fixed = true;
                    })
                    .on("dragend", function (d, i) {
                    //拖拽结束后变为原来的颜色
                    d3.select(this).style("fill", color(i));
                    })
                    .on("drag", function (d) {
                    //拖拽中对象变为黄色
                    d3.select(this).style("fill", "yellow");
                    });

      //绘制文字
      var texts = svg
        .selectAll(".forceText")
        .data(nodes) // 文本的数目与节点的数目相同
        .enter()
        .append("text")
        .attr("class", "forceText")
        .attr("x", function (d) {
          return d.x;
        })
        .attr("y", function (d) {
          return d.y;
        })
        .attr("dy", ".3em")
        .text(function (d) {
          return d.name; // 文本的内容即节点的名称
        });

      //tick事件的监听器
      force.on("tick", function () {
        //更新边的端点坐标
        lines.attr("x1", function (d) {
          return d.source.x;
        });
        lines.attr("y1", function (d) {
          return d.source.y;
        });
        lines.attr("x2", function (d) {
          return d.target.x;
        });
        lines.attr("y2", function (d) {
          return d.target.y;
        });

        //更新节点坐标
        circles.attr("cx", function (d) {
          return d.x;
        });
        circles.attr("cy", function (d) {
          return d.y;
        });

        //更新节点文字的坐标
        texts.attr("x", function (d) {
          return d.x;
        });
        texts.attr("y", function (d) {
          return d.y;
        });
      });

      //事件监听器：运动开始时
      force.on("start", function () {
        console.log("运动开始");
      });

      //运动结束时
      force.on("end", function () {
        console.log("运动结束");
      });
    </script>
  </body>
</html>
